#include "Sha1.h"

Sha1::Sha1() {

}

Sha1::~Sha1() {

}

int Sha1::leftrotate(unsigned int valor, unsigned int shift) {
	shift &= 31;
	return (valor << shift) | (valor >> (32 - shift));
}

void Sha1::appendIntToStr(string& palabra, unsigned int entero) {

	unsigned int ent = htonl(entero);
	char aux[4];
	memcpy(aux, &ent, 4);
	palabra.append(aux,4);
}

void Sha1::appendIntToStr(string& palabra, unsigned long long int entero) {
	unsigned long long int ent = entero;
	char aux[8];
	memcpy(aux, &ent, 8);
	for (int i = 7;i>=0; i--) {
		palabra += aux[i];
	}
}

unsigned char toUnsignedChar(char arg) {
	unsigned aux = arg;
	if (arg < 0) {
		return aux + 256;
	} else {
		return arg;
	}
}

int Sha1::strToInt(const string& palabra) {
	return toUnsignedChar(palabra[3]) + toUnsignedChar(palabra[2])*256 +
	toUnsignedChar(palabra[1])*65536 + toUnsignedChar(palabra[0])*16777216;
}

string Sha1::intToHexaStr(int arg) {
	char aux[33];
	sprintf(aux,"%x",arg);
	aux[32] = '\0';
	string resultado = aux;
	return resultado;
}

string Sha1::intToMemStr(int arg) {

	int ent = htonl(arg);
	char aux[5];
	memcpy(aux, &ent, 4);
	aux[4] = '\0';

	return aux;
}

string Sha1::calcularSha1(const string& entrada, bool hexa) {

	string mensaje = entrada;

	// inicializo los valores de hash. Htonl convierte a big endian
	unsigned int h0 = 0x67452301;
	unsigned int h1 = 0xEFCDAB89;
	unsigned int h2 = 0x98BADCFE;
	unsigned int h3 = 0x10325476;
	unsigned int h4 = 0xC3D2E1F0;

	unsigned long long int tamanioMensaje = mensaje.size()*8;

	// creo 8 bits con un uno y el resto ceros
	unsigned char aux1 = 0x80;
	unsigned char byteNulo = '\0';

	mensaje += aux1;

	while ((mensaje.size() + 8) % 64 != 0) {
		mensaje += byteNulo;
	}

	appendIntToStr(mensaje,tamanioMensaje);

	// divido el mensaje en pedazos de 64 bytes(512 bits)
	unsigned int cantChunks = mensaje.size() / 64;

	for (unsigned int iChunk = 0; iChunk<cantChunks; iChunk++) {

		// selecciono el chunk actual
		string chunk = mensaje.substr(iChunk*64,64);

		// creo 80 papabras w de 4 bytes(32 bits) cada una
		unsigned int w[80];

		// copio la memoria de a cuatro chars y los meto en ints
		for (unsigned int i = 0; i<16; i++) {
			w[i] = strToInt(chunk.substr(i*4,4));
		}

		// creo el resto del vector
		for (unsigned int i = 16; i<80; i++) {
			w[i] = leftrotate(w[i-3] ^ w[i-8] ^ w[i-14] ^ w[i-16],1);
		}

		unsigned int a = h0;
		unsigned int b = h1;
		unsigned int c = h2;
		unsigned int d = h3;
		unsigned int e = h4;

		unsigned int f;
		unsigned int k;

		// loop principal
		for (unsigned int i = 0; i<80; i++) {
			if (i<20) {
				f = (b & c) | ((~b) & d);
				k = 0x5A827999;
			} else if (i<40) {
				f = b ^ c ^ d;
				k = 0x6ED9EBA1;
			} else if (i<60) {
				f = (b & c) | (b & d) | (c & d);
				k = 0x8F1BBCDC;
			} else if (i<80) {
				f = b ^ c ^ d;
				k = 0xCA62C1D6;
			}

			unsigned int temp = leftrotate(a,5) + f + e + k + w[i];
			e = d;
			d = c;
			c = leftrotate(b,30);
			b = a;
			a = temp;

		}

		// actualizo los h
		h0 = h0 + a;
		h1 = h1 + b;
		h2 = h2 + c;
		h3 = h3 + d;
		h4 = h4 + e;
	}

	string resultado;

	if (hexa) {
	// imprimio los h en hexa y los meto en un string
	resultado += intToHexaStr(h0);
	resultado += intToHexaStr(h1);
	resultado += intToHexaStr(h2);
	resultado += intToHexaStr(h3);
	resultado += intToHexaStr(h4);
	} else {
	// copio la memoria de los ints y la meto en chars
	resultado += intToMemStr(h0);
	resultado += intToMemStr(h1);
	resultado += intToMemStr(h2);
	resultado += intToMemStr(h3);
	resultado += intToMemStr(h4);
	}

	return resultado;
}
